package collector

import (
	"sync"

	"github.com/prometheus/client_golang/prometheus"
)

const (
	Namespace = "pve"
)

type Collector struct {
	sync.Mutex

	pveAddr            string
	metricDescriptions map[string]*prometheus.Desc
	options            Options
}

type UserInfo struct {
	Username string
	Password string
}

type Options struct {
	UserInfo    UserInfo
	PasswordMap map[string]UserInfo
}

type MetricDesc struct {
	metric string
	txt    string
	labels []string
}

var infoDescs = map[string]MetricDesc{
	"vmup":         {metric: "vm_up", txt: `pve node up`, labels: []string{"id", "name"}},
	"nodeinfo":     {metric: "node_up", txt: `pve node info metric`, labels: []string{"id", "name"}},
	"clusterinfo":  {metric: "cluster_up", txt: `pve cluster info metric`, labels: []string{"name"}},
	"clusternodes": {metric: "cluster_nodes", txt: `Number of pve cluster nodes metric`, labels: []string{"name"}},
	"storageinfo":  {metric: "storage_info", txt: `Storage info`, labels: []string{"id", "node", "storage"}},
}

var resourceDescs = map[string]MetricDesc{
	"maxdisk":   {metric: "disk_size_bytes", txt: `Size of storage device`, labels: []string{"id"}},
	"disk":      {metric: "disk_usage_bytes", txt: `Disk usage in bytes`, labels: []string{"id"}},
	"maxmem":    {metric: "memory_size_bytes", txt: `Size of memory`, labels: []string{"id"}},
	"mem":       {metric: "memory_usage_bytes", txt: `Memory usage in bytes`, labels: []string{"id"}},
	"netout":    {metric: "network_transmit_bytes", txt: `Number of bytes transmitted over the network`, labels: []string{"id"}},
	"netin":     {metric: "network_receive_bytes", txt: `Number of bytes received over the network`, labels: []string{"id"}},
	"diskwrite": {metric: "disk_write_bytes", txt: `Number of bytes written to storage`, labels: []string{"id"}},
	"diskread":  {metric: "disk_read_bytes", txt: `Number of bytes read from storage`, labels: []string{"id"}},
	"cpu":       {metric: "cpu_usage_ratio", txt: `CPU usage (value between 0.0 and pve_cpu_usage_limit)`, labels: []string{"id"}},
	"maxcpu":    {metric: "cpu_usage_limit", txt: `Maximum allowed CPU usage`, labels: []string{"id"}},
	"uptime":    {metric: "uptime_seconds", txt: `Number of seconds since the last boot`, labels: []string{"id"}},
	"shared":    {metric: "storage_shared", txt: `Whether or not the storage is shared among cluster nodes`, labels: []string{"id"}},
}

func NewPveCollector(pveAddr string, opts Options) *Collector {
	pve := &Collector{
		options: opts,
		pveAddr: pveAddr,
	}

	pve.metricDescriptions = map[string]*prometheus.Desc{}

	for k, desc := range resourceDescs {
		pve.metricDescriptions[k] = newMetricDescr(desc.metric, desc.txt, desc.labels)
	}

	for k, desc := range infoDescs {
		pve.metricDescriptions[k] = newMetricDescr(desc.metric, desc.txt, desc.labels)
	}

	return pve
}

func newMetricDescr(metricName, docString string, labels []string) *prometheus.Desc {
	return prometheus.NewDesc(prometheus.BuildFQName(Namespace, "", metricName), docString, labels, nil)
}

func (c *Collector) Describe(ch chan<- *prometheus.Desc) {
	for _, desc := range c.metricDescriptions {
		ch <- desc
	}
}

func (c *Collector) Collect(ch chan<- prometheus.Metric) {
	c.Mutex.Lock()
	defer c.Mutex.Unlock()

	if c.pveAddr != "" {
		c.ScrapePveHost(ch)
	}

}
